#include "oclUtil.h"

#include <GL/gl.h>
#include <CL/cl_gl.h>

#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>

void _cluGetPlatformIDs(const char *fi, int li, cl_uint ne, cl_platform_id *p,
        cl_uint *np)
{
    cl_int err = clGetPlatformIDs(ne, p, np);
    if(err == CL_SUCCESS); return;

    std::cerr << "Error getting platform IDs in " <<
        fi << " on line " << li << ": ";

    cluParseErr(err);
    return;
}

void _cluPrintPlatformVersion(const char *fi, int li, cl_platform_id p){
    char buf[1024] = {0};

    cl_int err = clGetPlatformInfo(p, CL_PLATFORM_VERSION, sizeof(char)*1024,
            buf, NULL);

    if(err == CL_SUCCESS){
        std::cout << buf << std::endl;
        return;
    }

    std::cerr << "Error reading platform version in " << fi << " on line " 
        << li << ": ";

    cluParseErr(err);
}

cl_context _cluCreateContext(const char *fi, int li, 
        cl_context_properties *clcp, cl_uint numDevs, const cl_device_id *devs,
        void(*clcb)(const char *, const void *, size_t, void *), void *clud){
    cl_int err;
    cl_context cxt = clCreateContext(clcp, numDevs, devs, clcb, clud, &err);

    if(err != CL_SUCCESS){
        std::cerr << "Error creating context in " << fi << " on line " << li
            << ": ";
        cluParseErr(err);
    }
    return cxt;
}

cl_context _cluCreateContextFromType(const char *fi, int li, 
        cl_context_properties *clcp, cl_device_type cldt, 
        void (*clcb)(const char *, const void *, size_t, void *), 
        void *clud)
{
    cl_int err;
    cl_context cxt = clCreateContextFromType(clcp, cldt, clcb, clud, &err);
    if(err == CL_SUCCESS) return cxt;

    std::cerr << "Error creating context in " << 
        fi << " on line " << li << ": ";

    cluParseErr(err);
    return cxt;
}

void _cluGetDeviceIDs(const char *fi, int li, cl_platform_id plat,
        cl_device_type dt, cl_uint num, cl_device_id *devs, cl_uint *retnum){
    cl_int err;
    err = clGetDeviceIDs(plat, dt, num, devs, retnum);
    if(err == CL_SUCCESS) return;

    std::cerr << "Error getting device IDs in " << fi << " on line " << li
        << ": ";
    cluParseErr(err);
}

void _cluGetDeviceInfo(const char *fi, int li, cl_device_id dev,
        cl_device_info info, size_t infosz, void *val, size_t *valsz)
{
    cl_int err = clGetDeviceInfo(dev, info, infosz, val, valsz);
    if(err == CL_SUCCESS) return;

    std::cerr << "Error getting device info in " << fi << " on line " << li
        << ": ";
    cluParseErr(err);
}

void _cluGetContextInfo(const char * fi, int li, cl_context clct, 
        cl_context_info clci, size_t clsz, void *clvl, size_t *clsr)
{

    cl_int err = clGetContextInfo(clct, clci, clsz, clvl, clsr);
    if(err == CL_SUCCESS) return;

    std::cerr << "Error getting context info in " << 
        fi << " on line " << li << ": ";
    cluParseErr(err);
}
    
cl_command_queue _cluCreateCommandQueue(const char *fi, int li, cl_context clct, 
        cl_device_id clid, cl_command_queue_properties clpr)
{
    cl_int err;
    cl_command_queue clcq = clCreateCommandQueue(clct, clid, clpr, &err);
    if(err == CL_SUCCESS) return clcq;

    std::cerr << "Error creating command queue in " << 
        fi << " on line " << li << ": ";
    cluParseErr(err);
    return clcq;
}

cl_program _cluCreateProgramWithSource(const char *fi, int li, cl_context clct, 
        cl_uint clas, const char **clsr, const size_t *clls)
{
    cl_int err;
    cl_program clpr = clCreateProgramWithSource(clct, clas, clsr, clls, &err);
    if(err == CL_SUCCESS) return clpr;

    std::cerr << "Error loading program source in " <<
        fi << " on line " << li << ": ";
    cluParseErr(err);
    return clpr;
}

char *cluLoadProg(const char *filename){
    FILE *fp;
    int count = 0;
    char *src = NULL;

    std::string fn = std::string(filename);

    if( fn.length() > 0 ){
        fp = fopen(fn.c_str(), "rt");

        if( fp != NULL ){
            fseek(fp, 0, SEEK_END);
            count = ftell(fp);
            rewind(fp);

            if( count > 0 ){
                src = new char[count+1];
                count = fread(src, sizeof(char), count, fp);
                src[count] = '\0';
            }
            fclose(fp);
        }else{
            std::cerr << "Failure!\n" << "\tCould not open file \"" <<
                fn << "\"" << std::endl;
        }
    }else{
        std::cerr << "Failure\n" << "\tNull file name." << std::endl;
    }

    return src;
}

void cluCheckProgBuildLog(cl_program prog, cl_device_id did){
    //get log size
    int logSize = 0;
    clGetProgramBuildInfo(prog, did, CL_PROGRAM_BUILD_LOG,
            0, NULL, (size_t*)&logSize);
    if(logSize > 0){
        std::cout << "OpenCL Build log:";
        char *log = (char*)malloc(logSize);
        clGetProgramBuildInfo(prog, did, CL_PROGRAM_BUILD_LOG,
                logSize, (void*)log, NULL);

        std::cout << log << "\n" << std::endl;
        delete log;
    }
}

bool _cluBuildProgram(const char *fi, int li, cl_program clpr, cl_uint clnd, 
        const cl_device_id *cldl, const char *clop, 
        void (*clcb)(cl_program, void *), void *clud)
{
    cl_int err = clBuildProgram(clpr, clnd, cldl, clop, clcb, clud);
    if(err != CL_SUCCESS){
        std::cerr << "Error building program in " << 
            fi << " on line " << li << ": ";
        cluParseErr(err);
        cluCheckProgBuildLog(clpr, cldl[0]);
        return false;
    }

    //cluCheckProgBuildLog(clpr, cldl[0]);
    return true;
}

cl_mem _cluCreateBuffer(const char *fi, int li, cl_context clct, 
        cl_mem_flags clmf, size_t clsz, void *clhp)
{
    cl_int err;
    cl_mem buf = clCreateBuffer(clct, clmf, clsz, clhp, &err);
    if(err == CL_SUCCESS) return buf;

    std::cerr << "Error creating a buffer in " << 
        fi << " on line " << li << ": ";
    cluParseErr(err);

    return buf;
}

cl_mem _cluCreateImage2D(const char *fi, int li, cl_context clct,
        cl_mem_flags clmf, const cl_image_format *imgf, size_t iw, size_t ih,
        size_t iptch, void *clhp){
    cl_int err;
    cl_mem img = clCreateImage2D(clct, clmf, imgf, iw, ih, iptch, clhp, &err);

    if(err != CL_SUCCESS){
        std::cerr << "Error creating a 2D image in " << fi << " on line "
            << li << ": ";
        cluParseErr(err);
    }
    
    return img;
}

cl_mem _cluCreateImage3D(const char *fi, int li, cl_context clcx,
        cl_mem_flags clmf, const cl_image_format *imgf, size_t iw, size_t ih,
        size_t id, size_t rptch, size_t sptch, void *clhp){
    cl_int err;
    cl_mem img = clCreateImage3D(clcx, clmf, imgf, iw, ih, id, rptch, sptch, 
            clhp, &err);

    if(err != CL_SUCCESS){
        std::cerr << "Error creating a 3D image in " << fi << " on line "
            << li << ": ";
        cluParseErr(err);
    }

    return img;
}

cl_mem _cluCreateFromGLBuffer(const char *fi, int li, cl_context clcx,
        cl_mem_flags clmf, GLuint bufID){
    cl_int err;
    cl_mem buf = clCreateFromGLBuffer(clcx, clmf, bufID, &err);
    
    if(err != CL_SUCCESS){
        std::cerr << "Error creating CL buffer from GL buffer in " << fi
            << " on line " << li << ": ";
        cluParseErr(err);
    }

    return buf;
}

cl_mem _cluCreateFromGLTexture2D(const char *fi, int li, cl_context clcx,
        cl_mem_flags clmf, GLenum gltextarg, GLint miplvl, GLuint texID){
    cl_int err;
    cl_mem img = clCreateFromGLTexture2D(clcx, clmf, gltextarg, miplvl, texID,
            &err);
    if(err != CL_SUCCESS){
        std::cerr << "Error creating 2D image from GL 2D texture in " << fi
            << " on line " << li << ": ";
        cluParseErr(err);
    }
    
    return img;
}

cl_mem _cluCreateFromGLTexture3D(const char *fi, int li, cl_context clcx,
        cl_mem_flags clmf, GLenum gltextarg, GLint miplvl, GLuint texID){
    cl_int err;
    cl_mem img = clCreateFromGLTexture3D(clcx, clmf, gltextarg, miplvl, texID,
            &err);

    if(err != CL_SUCCESS){
        std::cerr << "Error creating 3D image from GL 3D texture in " << fi
            << " on line " << li << ": ";
        cluParseErr(err);
    }

    return img;
}

void _cluEnqueueAcquireGLObjects(const char *fi, int li,
        cl_command_queue clcmdq, cl_uint n, const cl_mem *objs, cl_uint en,
        const cl_event *eList, cl_event *e){
    cl_int err = clEnqueueAcquireGLObjects(clcmdq, n, objs, en, eList, e);

    if(err != CL_SUCCESS){
        std::cerr << "Error acquiring GL objects in " << fi << " on line " << li
            << ": ";
        cluParseErr(err);
    }
}

void _cluEnqueueReleaseGLObjects(const char *fi, int li,
        cl_command_queue clcmdq, cl_uint n, const cl_mem *objs, cl_uint en,
        const cl_event *eList, cl_event *e){
    cl_int err = clEnqueueReleaseGLObjects(clcmdq, n, objs, en, eList, e);

    if(err != CL_SUCCESS){
        std::cerr << "Error acquiring GL objects in " << fi << " on line " << li
            << ": ";
        cluParseErr(err);
    }
}

void _cluEnqueueReadImage(const char *fi, int li, cl_command_queue clcmdq,
        cl_mem climg, cl_bool blocking, const size_t *offs,
        const size_t *extent, size_t rowptch, size_t layerptch,
        void *img, cl_uint wlsz, const cl_event *wl, cl_event *event){
    
    cl_int err = clEnqueueReadImage(clcmdq, climg, blocking, offs, extent,
            rowptch, layerptch, img, wlsz, wl, event);

    if(err != CL_SUCCESS){
        std::cerr << "Error reading CL image object in " << fi << " on line "
            << li << ": ";
        cluParseErr(err);
    }
}

void _cluEnqueueWriteImage(const char *fi, int li, cl_command_queue clcmdq,
        cl_mem climg, cl_bool blocking, const size_t *offs,
        const size_t *extent, size_t rowptch, size_t layerptch,
        const void *img, cl_uint wlsz, const cl_event *wl, cl_event *event){

    cl_int err = clEnqueueWriteImage(clcmdq, climg, blocking, offs, extent,
            rowptch, layerptch, img, wlsz, wl, event);

    if(err != CL_SUCCESS){
        std::cerr << "Error writing CL image object in " << fi << " on line "
            << li << ": ";
        cluParseErr(err);
    }
}

void _cluGetMemObjectInfo(const char *fi, int li, cl_mem memObj, 
        cl_mem_info param, size_t paramSz, void *paramBuf, size_t *retSz){
    cl_int err = clGetMemObjectInfo(memObj, param, paramSz, paramBuf, retSz);

    if(err != CL_SUCCESS){
        std::cerr << "Error getting CL memory object info in " << fi 
            << " on line " << li << ": ";
        cluParseErr(err);
    }
}

void _cluReleaseMemObject(const char *fi, int li, cl_mem clmo){
    cl_int err = clReleaseMemObject(clmo);
    
    if(err == CL_SUCCESS) return;

    std::cerr << "Error releasing CL memory object in " <<
        fi << " on line " << li << ": ";

    cluParseErr(err);
}


void _cluReleaseKernel(const char *fi, int li, cl_kernel clk){
    cl_int err = clReleaseKernel(clk);

    if(err != CL_SUCCESS){
        std::cerr << "Error release CL kernel object in " << fi << " on line "
            << li << ": ";

        cluParseErr(err);
    }
}


void _cluReleaseProgram(const char *fi, int li, cl_program clk){
    cl_int err = clReleaseProgram(clk);

    if(err != CL_SUCCESS){
        std::cerr << "Error release CL program object in " << fi << " on line "
            << li << ": ";

        cluParseErr(err);
    }
}


void _cluReleaseCommandQueue(const char *fi, int li, cl_command_queue clk){
    cl_int err = clReleaseCommandQueue(clk);

    if(err != CL_SUCCESS){
        std::cerr << "Error release CL command queue object in " << fi
            << " on line " << li << ": ";

        cluParseErr(err);
    }
}


void _cluReleaseContext(const char *fi, int li, cl_context clk){
    cl_int err = clReleaseContext(clk);

    if(err != CL_SUCCESS){
        std::cerr << "Error release CL context object in " << fi << " on line "
            << li << ": ";

        cluParseErr(err);
    }
}


void _cluEnqueueWriteBuffer(const char *fi, int li, cl_command_queue clcq,
        cl_mem clbf, cl_bool clbl, size_t clof, size_t clsz, const void *clhp,
        cl_uint clne, const cl_event *clew, cl_event *clev)
{
    cl_int err = clEnqueueWriteBuffer(clcq, clbf, clbl, clof, clsz, clhp, 
            clne, clew, clev);
    if(err == CL_SUCCESS) return;

    std::cerr << "Error writing buffer in " << 
        fi << " on line " << li << ": ";
    cluParseErr(err);
}

void _cluEnqueueReadBuffer(const char *fi, int li, cl_command_queue clcq,
        cl_mem clbf, cl_bool clbl, size_t clof, size_t clsz, void *clhp,
        cl_uint clne, const cl_event *clew, cl_event *clev)
{
    cl_int err = clEnqueueReadBuffer(clcq, clbf, clbl, clof, clsz, clhp,
            clne, clew, clev);
    if(err == CL_SUCCESS) return;

    std::cerr << "Error writing buffer in " <<
        fi << " on line " << li << ": ";
    cluParseErr(err);
}

void _cluEnqueueCopyBuffer(const char *fi, int li, cl_command_queue clcq,
        cl_mem clsbf, cl_mem cldbf, size_t clsof, size_t cldof, size_t clsz,
        cl_uint clne, const cl_event *clew, cl_event *clev)
{
    cl_int err = clEnqueueCopyBuffer(clcq, clsbf, cldbf, clsof, cldof, clsz,
            clne, clew, clev);
    if(err == CL_SUCCESS) return;

    std::cerr << "Error writing buffer in " << fi << " on line " << li << ": ";
    cluParseErr(err);
}

cl_kernel _cluCreateKernel(const char *fi, int li, cl_program clpr,
        const char *clkn)
{
    cl_int err;
    cl_kernel kern = clCreateKernel(clpr, clkn, &err);
    if(err == CL_SUCCESS) return kern;

    std::cerr << "Error creating kernel in " <<
        fi << " on line " << li << ": ";
    cluParseErr(err);

    return kern;
}

void _cluSetKernelArg(const char *fi, int li, cl_kernel clkn, cl_uint clan,
        size_t clsz, const void *clav)
{
    cl_int err = clSetKernelArg(clkn, clan, clsz, clav);
    if(err == CL_SUCCESS) return;

    std::cerr << "Error setting kernel argument in " <<
        fi << " on line " << li << ": ";
    cluParseErr(err);
}

void _cluEnqueueNDRangeKernel(const char *fi, int li, cl_command_queue clcq,
        cl_kernel clkn, cl_uint cldm, const size_t *clof, const size_t *clgw,
        const size_t *cllw, cl_uint clne, const cl_event *clew, cl_event *clev)
{
    cl_int err = clEnqueueNDRangeKernel(clcq, clkn, cldm, clof, clgw, cllw, 
            clne, clew, clev);
    if(err == CL_SUCCESS) return;

    std::cerr << "Error queueing kernel in " <<
        fi << " on line " << li << ": ";

    cluParseErr(err);

    exit(1);
}

void _cluWaitForEvents(const char *fi, int li, 
        cl_uint num, const cl_event *list)
{
    cl_int err = clWaitForEvents(num, list);

    if(err == CL_SUCCESS) return;

    std::cerr << "Error queueing event wait in " << 
        fi << " on line " << li << ": ";

    cluParseErr(err);

    exit(1);
}

void _cluFinish(const char *fi, int li, cl_command_queue cq)
{
    cl_int err = clFinish(cq);

    if(err == CL_SUCCESS) return;

    std::cerr << "Error finishing queued events in " <<
        fi << " on line " << li << ": ";

    cluParseErr(err);

    exit(1);
}

void cluParseErr(cl_int err){
    switch(err){
        case CL_SUCCESS: return;
        case CL_DEVICE_NOT_FOUND:
            std::cerr << "Device not found."; break;
        case CL_DEVICE_NOT_AVAILABLE:
            std::cerr << "Device not available."; break;
        case CL_COMPILER_NOT_AVAILABLE:
            std::cerr << "Compiler not available."; break;
        case CL_MEM_OBJECT_ALLOCATION_FAILURE:
            std::cerr << "Memory object allocation failure."; break;
        case CL_OUT_OF_RESOURCES:
            std::cerr << "Out of resources."; break;
        case CL_OUT_OF_HOST_MEMORY:
            std::cerr << "Out of host memory."; break;
        case CL_PROFILING_INFO_NOT_AVAILABLE:
            std::cerr << "Profiling info not available."; break;
        case CL_MEM_COPY_OVERLAP:
            std::cerr << "Mem copy overlap."; break;
        case CL_IMAGE_FORMAT_MISMATCH:
            std::cerr << "Image format mismatch."; break;
        case CL_IMAGE_FORMAT_NOT_SUPPORTED:
            std::cerr << "Image format not supported."; break;
        case CL_BUILD_PROGRAM_FAILURE:
            std::cerr << "Build program failure."; break;
        case CL_MAP_FAILURE:
            std::cerr << "Map failure."; break;
        /*case CL_MISALIGNED_SUB_BUFFER_OFFSET:
            std::cerr << "Misaligned sub-buffer offset."; break;*/
        /*case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST:
            std::cerr << "Execution status error for wait list events.";break;*/
        case CL_INVALID_VALUE:
            std::cerr << "Invalid value."; break;
        case CL_INVALID_DEVICE_TYPE:
            std::cerr << "Invalid device type."; break;
        case CL_INVALID_PLATFORM:
            std::cerr << "Invalid platform."; break;
        case CL_INVALID_DEVICE:
            std::cerr << "Invalid device."; break;
        case CL_INVALID_CONTEXT:
            std::cerr << "Invalid context."; break;
        case CL_INVALID_QUEUE_PROPERTIES:
            std::cerr << "Invalid queue properties."; break;
        case CL_INVALID_COMMAND_QUEUE:
            std::cerr << "Invalid command queue."; break;
        case CL_INVALID_HOST_PTR:
            std::cerr << "Invalid host pointer."; break;
        case CL_INVALID_MEM_OBJECT:
            std::cerr << "Invalid memory object."; break;
        case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR:
            std::cerr << "Invalid image format descriptor."; break;
        case CL_INVALID_IMAGE_SIZE:
            std::cerr << "Invalid image size."; break;
        case CL_INVALID_SAMPLER:
            std::cerr << "Invalid sampler."; break;
        case CL_INVALID_BINARY:
            std::cerr << "Invalid binary."; break;
        case CL_INVALID_BUILD_OPTIONS:
            std::cerr << "Invalid build options."; break;
        case CL_INVALID_PROGRAM:
            std::cerr << "Invalid program."; break;
        case CL_INVALID_PROGRAM_EXECUTABLE:
            std::cerr << "Invalid program executable."; break;
        case CL_INVALID_KERNEL_NAME:
            std::cerr << "Invalid kernel name."; break;
        case CL_INVALID_KERNEL_DEFINITION:
            std::cerr << "Invalid kernel definition."; break;
        case CL_INVALID_KERNEL:
            std::cerr << "Invalid kernel."; break;
        case CL_INVALID_ARG_INDEX:
            std::cerr << "Invalid argument index."; break;
        case CL_INVALID_ARG_VALUE:
            std::cerr << "Invalid argument value."; break;
        case CL_INVALID_ARG_SIZE:
            std::cerr << "Invalid argument size."; break;
        case CL_INVALID_KERNEL_ARGS:
            std::cerr << "Invalid kernel arguments."; break;
        case CL_INVALID_WORK_DIMENSION:
            std::cerr << "Invalid work dimension."; break;
        case CL_INVALID_WORK_GROUP_SIZE:
            std::cerr << "Invalid work group size."; break;
        case CL_INVALID_WORK_ITEM_SIZE:
            std::cerr << "Invalid work item size."; break;
        case CL_INVALID_GLOBAL_OFFSET:
            std::cerr << "Invalid global offset."; break;
        case CL_INVALID_EVENT_WAIT_LIST:
            std::cerr << "Invalid event wait list."; break;
        case CL_INVALID_EVENT:
            std::cerr << "Invalid event."; break;
        case CL_INVALID_OPERATION:
            std::cerr << "Invalid operation."; break;
        case CL_INVALID_GL_OBJECT:
            std::cerr << "Invalid GL object."; break;
        case CL_INVALID_BUFFER_SIZE:
            std::cerr << "Invalid buffer size."; break;
        case CL_INVALID_MIP_LEVEL:
            std::cerr << "Invalid mip level."; break;
        case CL_INVALID_GLOBAL_WORK_SIZE:
            std::cerr << "Invalid globabl work size."; break;
        default:
            std::cerr << "Unrecognized error: " << err; break;
    }

    std::cerr << std::endl;
}

bool cluSupportsExtensions(cl_device_id dev, std::vector<std::string> extStrs){
    size_t extSize = 0;
    std::vector<bool> supported(extStrs.size(), false);

    //get the size of the extension string
    cluGetDeviceInfo(dev, CL_DEVICE_EXTENSIONS, 0, NULL, &extSize);

    //return false if there are no extensions supported (and at least one
    //extension was requested)
    if(extSize == 0 && extStrs.size() > 0) return false;

    //get the extension string
    char tmp[extSize];
    cluGetDeviceInfo(dev, CL_DEVICE_EXTENSIONS, extSize, tmp, &extSize);
    std::string extensions(tmp);

    std::cout << "\n" << extensions << std::endl;

    //determine which extensions are in the extension string
    size_t extBeg = 0;
    size_t extEnd = extensions.find(' ', 0); //space-delimited
    std::string ext;
    while(extBeg < extensions.size()){
        ext = extensions.substr(extBeg, extEnd-extBeg);
        for(unsigned int i=0; i<extStrs.size(); i++){
            supported[i] = supported[i] || extStrs[i].compare(ext)==0;
        }

        //skip any extra spaces
        do{
            extBeg = extEnd+1;
            extEnd = extensions.find(' ', extBeg);
        }while(extBeg == extEnd);
    }

    //check support for the requested extensions
    bool allSupported = true;
    for(unsigned int i=0; i<supported.size(); i++){
        allSupported = allSupported && supported[i];
        if(!supported[i]){
            std::cerr << "Extension \"" << extStrs[i] << "\" not found." 
                << std::endl;
        }
    }

    return allSupported;
}

bool cluSupportsExtension(cl_device_id dev, std::string extStr){
    size_t extSize = 0;
    bool supported = false;

    //get the size of the extension string
    cluGetDeviceInfo(dev, CL_DEVICE_EXTENSIONS, 0, NULL, &extSize);

    //return false if there are no extensions and extStr is not empty
    if(extSize == 0 && extStr.size() > 0) return false;

    //get the extension string
    char tmp[extSize];
    cluGetDeviceInfo(dev, CL_DEVICE_EXTENSIONS, extSize, tmp, &extSize);
    std::string extensions(tmp);

    //std::cout << "\n" << extensions << std::endl;

    //determine which extensions are in the extension string
    size_t extBeg = 0;
    size_t extEnd = extensions.find(' ', 0); //space-delimited
    std::string ext;
    while(extBeg < extensions.size()){
        ext = extensions.substr(extBeg, extEnd-extBeg);
        supported = supported || extStr.compare(ext)==0;
        /*
        for(unsigned int i=0; i<extStrs.size(); i++){
            supported[i] = supported[i] || extStrs[i].compare(ext)==0;
        }
        */

        //skip any extra spaces
        do{
            extBeg = extEnd+1;
            extEnd = extensions.find(' ', extBeg);
        }while(extBeg == extEnd);
    }

    //check support for the requested extensions
    //bool allSupported = true;
    //for(unsigned int i=0; i<supported.size(); i++){
        //allSupported = allSupported && supported[i];
        //if(!supported[i]){
    if(!supported){
        std::cerr << "Extension \"" << extStr << "\" not found." 
            << std::endl;
    }
    //}

    return supported;
}
